<?php

namespace Yolo\Components\ApplicationRegistry;

use Exception;
use GuzzleHttp\Exception\ClientException;
use Yolo\Components\RpcClient\Client;
use Yolo\Components\RpcClient\ClientInterface;
use Yolo\Di\Annotations\Initializer;
use Yolo\Di\Annotations\Singleton;
use Yolo\Errors\ConfigInvalidException;
use Yolo\Rpc\Annotations\AppScheme;
use Yolo\Service\ApplicationContainer;
use Yolo\Utils\Console;

#[Singleton]
class ApplicationRegistry
{
    const SERVICE_NAME = 'ApplicationRegistry';

    private ?ClientInterface $applicationRegistryClient;

    public function __construct(
        private readonly ApplicationContainer $container
    ){}

    #[Initializer]
    public function init(): void
    {
        $config = $this->getConfig();

        if (!$config || !$config['enabled']) {
            Console::log('Application Registry is disabled.', 'warning');
            return;
        }

        $url = $this->container->getConfig()->getClientConnection(ApplicationRegistry::SERVICE_NAME);
        if (!$url) {
            throw new ConfigInvalidException('Config rpc.client.connections.' . ApplicationRegistry::SERVICE_NAME . ' not found');
        }

        $this->applicationRegistryClient = new Client(
            self::SERVICE_NAME,
            $url
        );

    }

    /**
     * Register the service to Application Registry
     * @param AppScheme $appScheme
     * @return void
     * @throws ConfigInvalidException
     */
    public function register(AppScheme $appScheme): void
    {
        $config = $this->getConfig();

        if (!$config || !$config['enabled']) {
            Console::log('Application Registry is disabled.', 'warning');
            return;
        }

        $url = $this->container->getConfig()->getClientConnection(ApplicationRegistry::SERVICE_NAME);
        if (!$url) {
            throw new ConfigInvalidException('Config rpc.client.connections.' . ApplicationRegistry::SERVICE_NAME . ' not found');
        }

        Console::log("Application Registry server: $url");
        Console::log("Registering application \"{$appScheme->getServiceName()}\" to Application Registry...");

        $instance = $this->container->getConfig()->get('rpc.server.instance');
        if (empty($instance['http'])) {
            throw new ConfigInvalidException('Config rpc.server.instance.http not found');
        }

        try {
            $this->applicationRegistryClient->invoke('Register', [
                'body' => [
                    'instanceId' => $this->container->getConfig()->getInstanceId(),
                    'serviceName' => $appScheme->getServiceName(),
                    'instance' => $instance['http'],
                ]
            ]);
        } catch (ClientException $e) {

            $contents = $e->getResponse()->getBody()->getContents();
            $statusCode = $e->getResponse()->getStatusCode();

            Console::log("Failed to register the service, server response $statusCode error, response body: $contents", 'error');
        } catch (Exception $e) {

            Console::log("Failed to register the service, server response: {$e->getMessage()}", 'warning');
        }
    }

    /**
     * Discover a service from Application Registry
     * @param string $serviceName service name
     * @return array
     */
    public function discovery(string $serviceName): array
    {
        try {
            return $this->applicationRegistryClient->invoke('Discovery', [
                'body' => [
                    'serviceName' => $serviceName,
                ],
            ]);
        } catch (ClientException $e) {

            $contents = $e->getResponse()->getBody()->getContents();
            $statusCode = $e->getResponse()->getStatusCode();

            Console::log("Failed to discover the service, server response $statusCode error, response body: $contents", 'error');
        } catch (Exception $e) {

            Console::log("Failed to discover the service, server response: {$e->getMessage()}", 'error');
        }

        return [];
    }

    /**
     * 获取注册发现配置
     * @return array
     */
    public function getConfig(): array
    {
        return $this->container->getConfig()->get('applicationRegistry');
    }

    /**
     * 注册发现是否开启
     * @return false
     */
    public function enabled(): bool
    {
        $config = $this->container->getConfig()->get('applicationRegistry');

        return $config['enabled'] ?? false;
    }
}
